{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "b815ef73",
   "metadata": {},
   "source": [
    "# 工具和路由 Tools and Routing\n",
    "\n",
    " - [一、设置OpenAI API Key](#一、设置OpenAI-API-Key)\n",
    " - [二、通过Langchain定义工具](#二、通过Langchain定义工具)\n",
    "     - [2.1 通过装饰器之间定义Tool](#2.1-通过装饰器之间定义Tool)\n",
    "     - [2.2 通过pydantic类定义Tool](#2.2-通过pydantic类定义Tool)\n",
    "     - [2.3 天气查询应用案例](#2.3-天气查询应用案例)\n",
    "     - [2.4 通过tool定义function](#2.4-通过tool定义function)\n",
    "     - [2.5 通过API定义function案例](#2.5-通过API定义function案例)\n",
    " - [三、 路由](#三、路由)\n",
    "     - [3.1 Tool转换为Function](#3.1-Tool转换为Function)\n",
    "     - [3.2 通过route进行tools的选择](#3.2-通过route进行tools的选择)\n",
    "     - [3.3 输出解析器](#3.3-输出解析器)\n",
    " - [四、总结](#四、总结)\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "00b7c548",
   "metadata": {},
   "source": [
    "# 一、设置OpenAI-API-Key\n",
    "\n",
    "详细内容见`设置OpenAI_API_KEY.ipynb`文件"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "6466fd33",
   "metadata": {},
   "source": [
    "# 二、通过Langchain定义工具"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "032bdb49",
   "metadata": {},
   "source": [
    "## 2.1-通过装饰器之间定义Tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "744a1e2d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# tool装饰器包装了search函数\n",
    "@tool\n",
    "def search(query: str) -> str:\n",
    "    \"\"\"在网络上查询天气\"\"\"\n",
    "    return \"42度\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5b73d2ee",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "search\n",
      "search(query: str) -> str - 在网络上查询天气\n",
      "{'query': {'title': 'Query', 'type': 'string'}}\n"
     ]
    }
   ],
   "source": [
    "# 搜索工具的函数名\n",
    "print(search.name)\n",
    "#搜索工具的功能描述（即函数注释）\n",
    "print(search.description)\n",
    "# 搜索工具需要传递的参数\n",
    "print(search.args)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "58d2d5a5",
   "metadata": {},
   "source": [
    "## 2.2-通过pydantic类定义Tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "8765af16-17fc-4490-98ab-4e9dd59337cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入 Pydantic 库中的 BaseModel 类和 Field 函数，它们用于定义数据模型和字段。\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "class SearchInput(BaseModel):\n",
    "    \"\"\"\n",
    "    定义了 SearchInput 类中的一个属性 query，它是一个字符串类型。通过 Field 函数，你为这个字段提供了一些配置\n",
    "    其中 description 参数用于描述这个字段的用途，即 \"Thing to search for\"（要搜索的内容）。\n",
    "    \"\"\"\n",
    "    query: str = Field(description=\"你需要搜索的东西\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "88b3f7d0-a856-4842-82da-6c00df53e9d9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'query': {'title': 'Query', 'type': 'string'}}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 搜索工具类需要传递的参数\n",
    "search.args"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "dbea2637",
   "metadata": {},
   "outputs": [],
   "source": [
    "# args_schema参数传递SearchInput工具类\n",
    "@tool(args_schema=SearchInput)\n",
    "def search_zh(query: str) -> str:\n",
    "    \"\"\"在网上查找温度\"\"\"\n",
    "    return \"42度\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "fcbf965d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'query': {'title': 'Query', 'type': 'string'}}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "search.args"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "ec1937e3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'42度'"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "search.run(\"圣弗朗西斯科\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "65467dd0",
   "metadata": {},
   "source": [
    "## 2.3-天气查询应用案例\n",
    "整体代码逻辑：\n",
    "1. 使用 Pydantic 定义了输入类OpenMeteoInput，以及输入的两个参数（经度和纬度）的输入格式\n",
    "2. 定义了一个函数 get_current_temperature，该函数使用 OpenMeteo API 获取给定坐标位置的当前温度。\n",
    "3. get_current_temperature函数通过发送 HTTP 请求获取 API 响应，然后从响应中提取并计算出当前时间对应的温度。\n",
    "4. get_current_temperature函数返回一个字符串，其中包含了当前温度的信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "e93b85b2-1786-4f63-8e65-cd6a8b5c0b33",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入所需的库\n",
    "import requests\n",
    "from pydantic import BaseModel, Field\n",
    "import datetime\n",
    "\n",
    "# 定义输入类（input schema）\n",
    "class OpenMeteoInput(BaseModel):\n",
    "    latitude: float = Field(..., description=\"要获取天气数据的位置的纬度\") \n",
    "    longitude: float = Field(..., description=\"要获取天气数据的位置的经度\") \n",
    "\n",
    "# 使用 @tool 装饰器并指定输入模型\n",
    "@tool(args_schema=OpenMeteoInput)\n",
    "def get_current_temperature(latitude: float, longitude: float) -> dict:\n",
    "    \"\"\"\"获取给定坐标的温度\"\"\"\n",
    "    \n",
    "    # Open Meteo API 的URL\n",
    "    BASE_URL = \"https://api.open-meteo.com/v1/forecast\"\n",
    "    \n",
    "    # 请求参数\n",
    "    params = {\n",
    "        'latitude': latitude,\n",
    "        'longitude': longitude,\n",
    "        'hourly': 'temperature_2m',\n",
    "        'forecast_days': 1,\n",
    "    }\n",
    "\n",
    "    # 发送 API 请求\n",
    "    response = requests.get(BASE_URL, params=params)\n",
    "    \n",
    "    # 检查响应状态码\n",
    "    if response.status_code == 200:\n",
    "        # 解析 JSON 响应\n",
    "        results = response.json()\n",
    "    else:\n",
    "        # 处理请求失败的情况\n",
    "        raise Exception(f\"API Request failed with status code: {response.status_code}\")\n",
    "\n",
    "    # 获取当前 UTC 时间\n",
    "    current_utc_time = datetime.datetime.utcnow()\n",
    "    \n",
    "    # 将时间字符串转换为 datetime 对象\n",
    "    time_list = [datetime.datetime.fromisoformat(time_str.replace('Z', '+00:00')) for time_str in results['hourly']['time']]\n",
    "    \n",
    "    # 获取温度列表\n",
    "    temperature_list = results['hourly']['temperature_2m']\n",
    "    \n",
    "    # 找到最接近当前时间的索引\n",
    "    closest_time_index = min(range(len(time_list)), key=lambda i: abs(time_list[i] - current_utc_time))\n",
    "    \n",
    "    # 获取当前温度\n",
    "    current_temperature = temperature_list[closest_time_index]\n",
    "    \n",
    "    # 返回当前温度的字符串形式\n",
    "    return f'当前的温度是 {current_temperature}°C'\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "731ed353-a27d-42df-89a8-9767bafb23be",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get_current_temperature\n",
      "get_current_temperature(latitude: float, longitude: float) -> dict - \"获取给定坐标的温度\n",
      "{'latitude': {'title': 'Latitude', 'description': '要获取天气数据的位置的纬度', 'type': 'number'}, 'longitude': {'title': 'Longitude', 'description': '要获取天气数据的位置的经度', 'type': 'number'}}\n"
     ]
    }
   ],
   "source": [
    "# 工具的名字\n",
    "print(get_current_temperature.name)\n",
    "# 工具的功能描述\n",
    "print(get_current_temperature.description)\n",
    "# 工具的输入参数\n",
    "print(get_current_temperature.args)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "38cefc99",
   "metadata": {},
   "source": [
    "## 2.4-通过tool定义function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "36aacbec-9cdd-414a-b502-168cea350a30",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入openai的模板\n",
    "from langchain.tools.render import format_tool_to_openai_function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "6277b6a8-f197-4057-a96a-5ebb61e18520",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\anaconda\\Lib\\site-packages\\langchain_core\\_api\\deprecation.py:117: LangChainDeprecationWarning: The function `format_tool_to_openai_function` was deprecated in LangChain 0.1.16 and will be removed in 0.2.0. Use langchain_core.utils.function_calling.convert_to_openai_function() instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'name': 'get_current_temperature',\n",
       " 'description': 'get_current_temperature(latitude: float, longitude: float) -> dict - \"获取给定坐标的温度',\n",
       " 'parameters': {'type': 'object',\n",
       "  'properties': {'latitude': {'description': '要获取天气数据的位置的纬度',\n",
       "    'type': 'number'},\n",
       "   'longitude': {'description': '要获取天气数据的位置的经度', 'type': 'number'}},\n",
       "  'required': ['latitude', 'longitude']}}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将定义好的工具直接传入模板，打印tool的名字、描述和输入参数格式\n",
    "format_tool_to_openai_function(get_current_temperature)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "e088eceb-424b-4da6-981f-63322c9ac56a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当前的温度是 41.4°C'"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用工具\n",
    "get_current_temperature({\"latitude\": 13, \"longitude\": 14})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "5eefc272-d205-4bad-ab00-282ccb3cc2fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "import wikipedia\n",
    "\n",
    "# 定义维基百科搜索的tool\n",
    "@tool\n",
    "def search_wikipedia(query: str) -> str:\n",
    "    \"\"\"打开维基百科搜索并获得页面的摘要\"\"\"\n",
    "    page_titles = wikipedia.search(query)\n",
    "    summaries = []\n",
    "    for page_title in page_titles[: 3]: #取前三个页面标题\n",
    "        try:\n",
    "            #使用 wikipedia 模块的 page 函数，获取指定标题的维基百科页面对象。\n",
    "            wiki_page =  wikipedia.page(title=page_title, auto_suggest=False) \n",
    "            # 获取页面摘要\n",
    "            summaries.append(f\"页面: {page_title}\\n摘要: {wiki_page.summary}\")\n",
    "        except (\n",
    "            self.wiki_client.exceptions.PageError,\n",
    "            self.wiki_client.exceptions.DisambiguationError,\n",
    "        ):\n",
    "            pass\n",
    "    if not summaries:\n",
    "        return \"维基百科没有搜索到合适的结果\"\n",
    "    return \"\\n\\n\".join(summaries)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "289c5d0c-0e71-4d70-963f-9d9f58e53d56",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'search_wikipedia'"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 工具的名字\n",
    "search_wikipedia.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "69756d79-ec2f-4185-89af-d14de817fc27",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'search_wikipedia(query: str) -> str - 打开维基百科搜索并获得页面的摘要'"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 工具的描述\n",
    "search_wikipedia.description"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "d5b876cd-cc6c-484d-a3fa-d8810e22d564",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': 'search_wikipedia',\n",
       " 'description': 'search_wikipedia(query: str) -> str - 打开维基百科搜索并获得页面的摘要',\n",
       " 'parameters': {'type': 'object',\n",
       "  'properties': {'query': {'type': 'string'}},\n",
       "  'required': ['query']}}"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将工具格式化为 OpenAI 函数\n",
    "format_tool_to_openai_function(search_wikipedia)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "6dfb10e9-5d1e-474a-9c8f-7abdf7939a34",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'self' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTimeoutError\u001b[0m                              Traceback (most recent call last)",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\urllib3\\util\\connection.py:85\u001b[0m, in \u001b[0;36mcreate_connection\u001b[1;34m(address, timeout, source_address, socket_options)\u001b[0m\n\u001b[0;32m     84\u001b[0m     sock\u001b[39m.\u001b[39mbind(source_address)\n\u001b[1;32m---> 85\u001b[0m sock\u001b[39m.\u001b[39mconnect(sa)\n\u001b[0;32m     86\u001b[0m \u001b[39mreturn\u001b[39;00m sock\n",
      "\u001b[1;31mTimeoutError\u001b[0m: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应，连接尝试失败。",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[22], line 14\u001b[0m, in \u001b[0;36msearch_wikipedia\u001b[1;34m(query)\u001b[0m\n\u001b[0;32m     13\u001b[0m     \u001b[39m# 获取页面摘要\u001b[39;00m\n\u001b[1;32m---> 14\u001b[0m     summaries\u001b[39m.\u001b[39mappend(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mPage: \u001b[39m\u001b[39m{\u001b[39;00mpage_title\u001b[39m}\u001b[39;00m\u001b[39m\\n\u001b[39;00m\u001b[39mSummary: \u001b[39m\u001b[39m{\u001b[39;00mwiki_page\u001b[39m.\u001b[39msummary\u001b[39m}\u001b[39;00m\u001b[39m\"\u001b[39m)\n\u001b[0;32m     15\u001b[0m \u001b[39mexcept\u001b[39;00m (\n\u001b[0;32m     16\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwiki_client\u001b[39m.\u001b[39mexceptions\u001b[39m.\u001b[39mPageError,\n\u001b[0;32m     17\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwiki_client\u001b[39m.\u001b[39mexceptions\u001b[39m.\u001b[39mDisambiguationError,\n\u001b[0;32m     18\u001b[0m ):\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\wikipedia\\wikipedia.py:530\u001b[0m, in \u001b[0;36mWikipediaPage.summary\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    528\u001b[0m    query_params[\u001b[39m'\u001b[39m\u001b[39mpageids\u001b[39m\u001b[39m'\u001b[39m] \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mpageid\n\u001b[1;32m--> 530\u001b[0m request \u001b[39m=\u001b[39m _wiki_request(query_params)\n\u001b[0;32m    531\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_summary \u001b[39m=\u001b[39m request[\u001b[39m'\u001b[39m\u001b[39mquery\u001b[39m\u001b[39m'\u001b[39m][\u001b[39m'\u001b[39m\u001b[39mpages\u001b[39m\u001b[39m'\u001b[39m][\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mpageid][\u001b[39m'\u001b[39m\u001b[39mextract\u001b[39m\u001b[39m'\u001b[39m]\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\wikipedia\\wikipedia.py:737\u001b[0m, in \u001b[0;36m_wiki_request\u001b[1;34m(params)\u001b[0m\n\u001b[0;32m    735\u001b[0m   time\u001b[39m.\u001b[39msleep(\u001b[39mint\u001b[39m(wait_time\u001b[39m.\u001b[39mtotal_seconds()))\n\u001b[1;32m--> 737\u001b[0m r \u001b[39m=\u001b[39m requests\u001b[39m.\u001b[39mget(API_URL, params\u001b[39m=\u001b[39mparams, headers\u001b[39m=\u001b[39mheaders)\n\u001b[0;32m    739\u001b[0m \u001b[39mif\u001b[39;00m RATE_LIMIT:\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\api.py:73\u001b[0m, in \u001b[0;36mget\u001b[1;34m(url, params, **kwargs)\u001b[0m\n\u001b[0;32m     63\u001b[0m \u001b[39m\u001b[39m\u001b[39mr\u001b[39m\u001b[39m\"\"\"Sends a GET request.\u001b[39;00m\n\u001b[0;32m     64\u001b[0m \n\u001b[0;32m     65\u001b[0m \u001b[39m:param url: URL for the new :class:`Request` object.\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     70\u001b[0m \u001b[39m:rtype: requests.Response\u001b[39;00m\n\u001b[0;32m     71\u001b[0m \u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m---> 73\u001b[0m \u001b[39mreturn\u001b[39;00m request(\u001b[39m\"\u001b[39m\u001b[39mget\u001b[39m\u001b[39m\"\u001b[39m, url, params\u001b[39m=\u001b[39mparams, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\api.py:59\u001b[0m, in \u001b[0;36mrequest\u001b[1;34m(method, url, **kwargs)\u001b[0m\n\u001b[0;32m     58\u001b[0m \u001b[39mwith\u001b[39;00m sessions\u001b[39m.\u001b[39mSession() \u001b[39mas\u001b[39;00m session:\n\u001b[1;32m---> 59\u001b[0m     \u001b[39mreturn\u001b[39;00m session\u001b[39m.\u001b[39mrequest(method\u001b[39m=\u001b[39mmethod, url\u001b[39m=\u001b[39murl, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\sessions.py:589\u001b[0m, in \u001b[0;36mSession.request\u001b[1;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001b[0m\n\u001b[0;32m    588\u001b[0m send_kwargs\u001b[39m.\u001b[39mupdate(settings)\n\u001b[1;32m--> 589\u001b[0m resp \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39msend(prep, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39msend_kwargs)\n\u001b[0;32m    591\u001b[0m \u001b[39mreturn\u001b[39;00m resp\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\sessions.py:725\u001b[0m, in \u001b[0;36mSession.send\u001b[1;34m(self, request, **kwargs)\u001b[0m\n\u001b[0;32m    724\u001b[0m     gen \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mresolve_redirects(r, request, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n\u001b[1;32m--> 725\u001b[0m     history \u001b[39m=\u001b[39m [resp \u001b[39mfor\u001b[39;00m resp \u001b[39min\u001b[39;00m gen]\n\u001b[0;32m    726\u001b[0m \u001b[39melse\u001b[39;00m:\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\sessions.py:725\u001b[0m, in \u001b[0;36m<listcomp>\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m    724\u001b[0m     gen \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mresolve_redirects(r, request, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n\u001b[1;32m--> 725\u001b[0m     history \u001b[39m=\u001b[39m [resp \u001b[39mfor\u001b[39;00m resp \u001b[39min\u001b[39;00m gen]\n\u001b[0;32m    726\u001b[0m \u001b[39melse\u001b[39;00m:\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\sessions.py:266\u001b[0m, in \u001b[0;36mSessionRedirectMixin.resolve_redirects\u001b[1;34m(self, resp, req, stream, timeout, verify, cert, proxies, yield_requests, **adapter_kwargs)\u001b[0m\n\u001b[0;32m    264\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[1;32m--> 266\u001b[0m     resp \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39msend(\n\u001b[0;32m    267\u001b[0m         req,\n\u001b[0;32m    268\u001b[0m         stream\u001b[39m=\u001b[39mstream,\n\u001b[0;32m    269\u001b[0m         timeout\u001b[39m=\u001b[39mtimeout,\n\u001b[0;32m    270\u001b[0m         verify\u001b[39m=\u001b[39mverify,\n\u001b[0;32m    271\u001b[0m         cert\u001b[39m=\u001b[39mcert,\n\u001b[0;32m    272\u001b[0m         proxies\u001b[39m=\u001b[39mproxies,\n\u001b[0;32m    273\u001b[0m         allow_redirects\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m,\n\u001b[0;32m    274\u001b[0m         \u001b[39m*\u001b[39m\u001b[39m*\u001b[39madapter_kwargs,\n\u001b[0;32m    275\u001b[0m     )\n\u001b[0;32m    277\u001b[0m     extract_cookies_to_jar(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mcookies, prepared_request, resp\u001b[39m.\u001b[39mraw)\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\sessions.py:703\u001b[0m, in \u001b[0;36mSession.send\u001b[1;34m(self, request, **kwargs)\u001b[0m\n\u001b[0;32m    702\u001b[0m \u001b[39m# Send the request\u001b[39;00m\n\u001b[1;32m--> 703\u001b[0m r \u001b[39m=\u001b[39m adapter\u001b[39m.\u001b[39msend(request, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n\u001b[0;32m    705\u001b[0m \u001b[39m# Total elapsed time of the request (approximately)\u001b[39;00m\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\requests\\adapters.py:486\u001b[0m, in \u001b[0;36mHTTPAdapter.send\u001b[1;34m(self, request, stream, timeout, verify, cert, proxies)\u001b[0m\n\u001b[0;32m    485\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m--> 486\u001b[0m     resp \u001b[39m=\u001b[39m conn\u001b[39m.\u001b[39murlopen(\n\u001b[0;32m    487\u001b[0m         method\u001b[39m=\u001b[39mrequest\u001b[39m.\u001b[39mmethod,\n\u001b[0;32m    488\u001b[0m         url\u001b[39m=\u001b[39murl,\n\u001b[0;32m    489\u001b[0m         body\u001b[39m=\u001b[39mrequest\u001b[39m.\u001b[39mbody,\n\u001b[0;32m    490\u001b[0m         headers\u001b[39m=\u001b[39mrequest\u001b[39m.\u001b[39mheaders,\n\u001b[0;32m    491\u001b[0m         redirect\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m,\n\u001b[0;32m    492\u001b[0m         assert_same_host\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m,\n\u001b[0;32m    493\u001b[0m         preload_content\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m,\n\u001b[0;32m    494\u001b[0m         decode_content\u001b[39m=\u001b[39m\u001b[39mFalse\u001b[39;00m,\n\u001b[0;32m    495\u001b[0m         retries\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mmax_retries,\n\u001b[0;32m    496\u001b[0m         timeout\u001b[39m=\u001b[39mtimeout,\n\u001b[0;32m    497\u001b[0m         chunked\u001b[39m=\u001b[39mchunked,\n\u001b[0;32m    498\u001b[0m     )\n\u001b[0;32m    500\u001b[0m \u001b[39mexcept\u001b[39;00m (ProtocolError, \u001b[39mOSError\u001b[39;00m) \u001b[39mas\u001b[39;00m err:\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\urllib3\\connectionpool.py:714\u001b[0m, in \u001b[0;36mHTTPConnectionPool.urlopen\u001b[1;34m(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)\u001b[0m\n\u001b[0;32m    713\u001b[0m \u001b[39m# Make the request on the httplib connection object.\u001b[39;00m\n\u001b[1;32m--> 714\u001b[0m httplib_response \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_make_request(\n\u001b[0;32m    715\u001b[0m     conn,\n\u001b[0;32m    716\u001b[0m     method,\n\u001b[0;32m    717\u001b[0m     url,\n\u001b[0;32m    718\u001b[0m     timeout\u001b[39m=\u001b[39mtimeout_obj,\n\u001b[0;32m    719\u001b[0m     body\u001b[39m=\u001b[39mbody,\n\u001b[0;32m    720\u001b[0m     headers\u001b[39m=\u001b[39mheaders,\n\u001b[0;32m    721\u001b[0m     chunked\u001b[39m=\u001b[39mchunked,\n\u001b[0;32m    722\u001b[0m )\n\u001b[0;32m    724\u001b[0m \u001b[39m# If we're going to release the connection in ``finally:``, then\u001b[39;00m\n\u001b[0;32m    725\u001b[0m \u001b[39m# the response doesn't need to know about the connection. Otherwise\u001b[39;00m\n\u001b[0;32m    726\u001b[0m \u001b[39m# it will also try to release it and we'll have a double-release\u001b[39;00m\n\u001b[0;32m    727\u001b[0m \u001b[39m# mess.\u001b[39;00m\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\urllib3\\connectionpool.py:403\u001b[0m, in \u001b[0;36mHTTPConnectionPool._make_request\u001b[1;34m(self, conn, method, url, timeout, chunked, **httplib_request_kw)\u001b[0m\n\u001b[0;32m    402\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m--> 403\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_validate_conn(conn)\n\u001b[0;32m    404\u001b[0m \u001b[39mexcept\u001b[39;00m (SocketTimeout, BaseSSLError) \u001b[39mas\u001b[39;00m e:\n\u001b[0;32m    405\u001b[0m     \u001b[39m# Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout.\u001b[39;00m\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\urllib3\\connectionpool.py:1053\u001b[0m, in \u001b[0;36mHTTPSConnectionPool._validate_conn\u001b[1;34m(self, conn)\u001b[0m\n\u001b[0;32m   1052\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mgetattr\u001b[39m(conn, \u001b[39m\"\u001b[39m\u001b[39msock\u001b[39m\u001b[39m\"\u001b[39m, \u001b[39mNone\u001b[39;00m):  \u001b[39m# AppEngine might not have  `.sock`\u001b[39;00m\n\u001b[1;32m-> 1053\u001b[0m     conn\u001b[39m.\u001b[39mconnect()\n\u001b[0;32m   1055\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m conn\u001b[39m.\u001b[39mis_verified:\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\urllib3\\connection.py:363\u001b[0m, in \u001b[0;36mHTTPSConnection.connect\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    361\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mconnect\u001b[39m(\u001b[39mself\u001b[39m):\n\u001b[0;32m    362\u001b[0m     \u001b[39m# Add certificate verification\u001b[39;00m\n\u001b[1;32m--> 363\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39msock \u001b[39m=\u001b[39m conn \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_new_conn()\n\u001b[0;32m    364\u001b[0m     hostname \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mhost\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\urllib3\\connection.py:174\u001b[0m, in \u001b[0;36mHTTPConnection._new_conn\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    173\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[1;32m--> 174\u001b[0m     conn \u001b[39m=\u001b[39m connection\u001b[39m.\u001b[39mcreate_connection(\n\u001b[0;32m    175\u001b[0m         (\u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_dns_host, \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mport), \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mtimeout, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mextra_kw\n\u001b[0;32m    176\u001b[0m     )\n\u001b[0;32m    178\u001b[0m \u001b[39mexcept\u001b[39;00m SocketTimeout:\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\urllib3\\util\\connection.py:91\u001b[0m, in \u001b[0;36mcreate_connection\u001b[1;34m(address, timeout, source_address, socket_options)\u001b[0m\n\u001b[0;32m     90\u001b[0m \u001b[39mif\u001b[39;00m sock \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m:\n\u001b[1;32m---> 91\u001b[0m     sock\u001b[39m.\u001b[39mclose()\n\u001b[0;32m     92\u001b[0m     sock \u001b[39m=\u001b[39m \u001b[39mNone\u001b[39;00m\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\socket.py:499\u001b[0m, in \u001b[0;36msocket.close\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    497\u001b[0m     _ss\u001b[39m.\u001b[39mclose(\u001b[39mself\u001b[39m)\n\u001b[1;32m--> 499\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mclose\u001b[39m(\u001b[39mself\u001b[39m):\n\u001b[0;32m    500\u001b[0m     \u001b[39m# This function should not reference any globals. See issue #808164.\u001b[39;00m\n\u001b[0;32m    501\u001b[0m     \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_closed \u001b[39m=\u001b[39m \u001b[39mTrue\u001b[39;00m\n",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m: ",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[27], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[39m# 调用\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m search_wikipedia({\u001b[39m\"\u001b[39m\u001b[39mquery\u001b[39m\u001b[39m\"\u001b[39m: \u001b[39m\"\u001b[39m\u001b[39mlangchain\u001b[39m\u001b[39m\"\u001b[39m})\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\langchain_core\\tools.py:519\u001b[0m, in \u001b[0;36mBaseTool.__call__\u001b[1;34m(self, tool_input, callbacks)\u001b[0m\n\u001b[0;32m    517\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m__call__\u001b[39m(\u001b[39mself\u001b[39m, tool_input: \u001b[39mstr\u001b[39m, callbacks: Callbacks \u001b[39m=\u001b[39m \u001b[39mNone\u001b[39;00m) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m \u001b[39mstr\u001b[39m:\n\u001b[0;32m    518\u001b[0m \u001b[39m    \u001b[39m\u001b[39m\"\"\"Make tool callable.\"\"\"\u001b[39;00m\n\u001b[1;32m--> 519\u001b[0m     \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mrun(tool_input, callbacks\u001b[39m=\u001b[39mcallbacks)\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\langchain_core\\tools.py:419\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[1;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, **kwargs)\u001b[0m\n\u001b[0;32m    417\u001b[0m \u001b[39mexcept\u001b[39;00m (\u001b[39mException\u001b[39;00m, \u001b[39mKeyboardInterrupt\u001b[39;00m) \u001b[39mas\u001b[39;00m e:\n\u001b[0;32m    418\u001b[0m     run_manager\u001b[39m.\u001b[39mon_tool_error(e)\n\u001b[1;32m--> 419\u001b[0m     \u001b[39mraise\u001b[39;00m e\n\u001b[0;32m    420\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m    421\u001b[0m     run_manager\u001b[39m.\u001b[39mon_tool_end(\n\u001b[0;32m    422\u001b[0m         \u001b[39mstr\u001b[39m(observation), color\u001b[39m=\u001b[39mcolor, name\u001b[39m=\u001b[39m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mname, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs\n\u001b[0;32m    423\u001b[0m     )\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\langchain_core\\tools.py:376\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[1;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, **kwargs)\u001b[0m\n\u001b[0;32m    373\u001b[0m     parsed_input \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_parse_input(tool_input)\n\u001b[0;32m    374\u001b[0m     tool_args, tool_kwargs \u001b[39m=\u001b[39m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_to_args_and_kwargs(parsed_input)\n\u001b[0;32m    375\u001b[0m     observation \u001b[39m=\u001b[39m (\n\u001b[1;32m--> 376\u001b[0m         \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_run(\u001b[39m*\u001b[39mtool_args, run_manager\u001b[39m=\u001b[39mrun_manager, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mtool_kwargs)\n\u001b[0;32m    377\u001b[0m         \u001b[39mif\u001b[39;00m new_arg_supported\n\u001b[0;32m    378\u001b[0m         \u001b[39melse\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_run(\u001b[39m*\u001b[39mtool_args, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mtool_kwargs)\n\u001b[0;32m    379\u001b[0m     )\n\u001b[0;32m    380\u001b[0m \u001b[39mexcept\u001b[39;00m ValidationError \u001b[39mas\u001b[39;00m e:\n\u001b[0;32m    381\u001b[0m     \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mhandle_validation_error:\n",
      "File \u001b[1;32md:\\anaconda\\Lib\\site-packages\\langchain_core\\tools.py:701\u001b[0m, in \u001b[0;36mStructuredTool._run\u001b[1;34m(self, run_manager, *args, **kwargs)\u001b[0m\n\u001b[0;32m    692\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfunc:\n\u001b[0;32m    693\u001b[0m     new_argument_supported \u001b[39m=\u001b[39m signature(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfunc)\u001b[39m.\u001b[39mparameters\u001b[39m.\u001b[39mget(\u001b[39m\"\u001b[39m\u001b[39mcallbacks\u001b[39m\u001b[39m\"\u001b[39m)\n\u001b[0;32m    694\u001b[0m     \u001b[39mreturn\u001b[39;00m (\n\u001b[0;32m    695\u001b[0m         \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfunc(\n\u001b[0;32m    696\u001b[0m             \u001b[39m*\u001b[39margs,\n\u001b[0;32m    697\u001b[0m             callbacks\u001b[39m=\u001b[39mrun_manager\u001b[39m.\u001b[39mget_child() \u001b[39mif\u001b[39;00m run_manager \u001b[39melse\u001b[39;00m \u001b[39mNone\u001b[39;00m,\n\u001b[0;32m    698\u001b[0m             \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs,\n\u001b[0;32m    699\u001b[0m         )\n\u001b[0;32m    700\u001b[0m         \u001b[39mif\u001b[39;00m new_argument_supported\n\u001b[1;32m--> 701\u001b[0m         \u001b[39melse\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfunc(\u001b[39m*\u001b[39margs, \u001b[39m*\u001b[39m\u001b[39m*\u001b[39mkwargs)\n\u001b[0;32m    702\u001b[0m     )\n\u001b[0;32m    703\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mNotImplementedError\u001b[39;00m(\u001b[39m\"\u001b[39m\u001b[39mTool does not support sync\u001b[39m\u001b[39m\"\u001b[39m)\n",
      "Cell \u001b[1;32mIn[22], line 16\u001b[0m, in \u001b[0;36msearch_wikipedia\u001b[1;34m(query)\u001b[0m\n\u001b[0;32m     13\u001b[0m         \u001b[39m# 获取页面摘要\u001b[39;00m\n\u001b[0;32m     14\u001b[0m         summaries\u001b[39m.\u001b[39mappend(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39mPage: \u001b[39m\u001b[39m{\u001b[39;00mpage_title\u001b[39m}\u001b[39;00m\u001b[39m\\n\u001b[39;00m\u001b[39mSummary: \u001b[39m\u001b[39m{\u001b[39;00mwiki_page\u001b[39m.\u001b[39msummary\u001b[39m}\u001b[39;00m\u001b[39m\"\u001b[39m)\n\u001b[0;32m     15\u001b[0m     \u001b[39mexcept\u001b[39;00m (\n\u001b[1;32m---> 16\u001b[0m         \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwiki_client\u001b[39m.\u001b[39mexceptions\u001b[39m.\u001b[39mPageError,\n\u001b[0;32m     17\u001b[0m         \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mwiki_client\u001b[39m.\u001b[39mexceptions\u001b[39m.\u001b[39mDisambiguationError,\n\u001b[0;32m     18\u001b[0m     ):\n\u001b[0;32m     19\u001b[0m         \u001b[39mpass\u001b[39;00m\n\u001b[0;32m     20\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m summaries:\n",
      "\u001b[1;31mNameError\u001b[0m: name 'self' is not defined"
     ]
    }
   ],
   "source": [
    "# 调用\n",
    "search_wikipedia({\"query\": \"langchain\"})"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "71add465",
   "metadata": {},
   "source": [
    "## 2.5-通过API定义function案例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "54cbe57e-f64c-4392-bcdd-9621fe1e46e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# openapi_spec_to_openai_fn可以把json格式的API定义转换成openai的function call格式\n",
    "from langchain.chains.openai_functions.openapi import openapi_spec_to_openai_fn\n",
    "# OpenAPISpec是标准化的API格式定义\n",
    "from langchain.utilities.openapi import OpenAPISpec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "7fe38e50-874c-49bd-8681-f95dcab0b464",
   "metadata": {},
   "outputs": [],
   "source": [
    "# json格式的API定义\n",
    "text = \"\"\"\n",
    "{\n",
    "  \"openapi\": \"3.0.0\",\n",
    "  \"info\": {\n",
    "    \"version\": \"1.0.0\",\n",
    "    \"title\": \"Swagger Petstore\",\n",
    "    \"license\": {\n",
    "      \"name\": \"MIT\"\n",
    "    }\n",
    "  },\n",
    "  \"servers\": [\n",
    "    {\n",
    "      \"url\": \"http://petstore.swagger.io/v1\"\n",
    "    }\n",
    "  ],\n",
    "  \"paths\": {\n",
    "    \"/pets\": {\n",
    "      \"get\": {\n",
    "        \"summary\": \"List all pets\",\n",
    "        \"operationId\": \"listPets\",\n",
    "        \"tags\": [\n",
    "          \"pets\"\n",
    "        ],\n",
    "        \"parameters\": [\n",
    "          {\n",
    "            \"name\": \"limit\",\n",
    "            \"in\": \"query\",\n",
    "            \"description\": \"How many items to return at one time (max 100)\",\n",
    "            \"required\": false,\n",
    "            \"schema\": {\n",
    "              \"type\": \"integer\",\n",
    "              \"maximum\": 100,\n",
    "              \"format\": \"int32\"\n",
    "            }\n",
    "          }\n",
    "        ],\n",
    "        \"responses\": {\n",
    "          \"200\": {\n",
    "            \"description\": \"A paged array of pets\",\n",
    "            \"headers\": {\n",
    "              \"x-next\": {\n",
    "                \"description\": \"A link to the next page of responses\",\n",
    "                \"schema\": {\n",
    "                  \"type\": \"string\"\n",
    "                }\n",
    "              }\n",
    "            },\n",
    "            \"content\": {\n",
    "              \"application/json\": {\n",
    "                \"schema\": {\n",
    "                  \"$ref\": \"#/components/schemas/Pets\"\n",
    "                }\n",
    "              }\n",
    "            }\n",
    "          },\n",
    "          \"default\": {\n",
    "            \"description\": \"unexpected error\",\n",
    "            \"content\": {\n",
    "              \"application/json\": {\n",
    "                \"schema\": {\n",
    "                  \"$ref\": \"#/components/schemas/Error\"\n",
    "                }\n",
    "              }\n",
    "            }\n",
    "          }\n",
    "        }\n",
    "      },\n",
    "      \"post\": {\n",
    "        \"summary\": \"Create a pet\",\n",
    "        \"operationId\": \"createPets\",\n",
    "        \"tags\": [\n",
    "          \"pets\"\n",
    "        ],\n",
    "        \"responses\": {\n",
    "          \"201\": {\n",
    "            \"description\": \"Null response\"\n",
    "          },\n",
    "          \"default\": {\n",
    "            \"description\": \"unexpected error\",\n",
    "            \"content\": {\n",
    "              \"application/json\": {\n",
    "                \"schema\": {\n",
    "                  \"$ref\": \"#/components/schemas/Error\"\n",
    "                }\n",
    "              }\n",
    "            }\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    },\n",
    "    \"/pets/{petId}\": {\n",
    "      \"get\": {\n",
    "        \"summary\": \"Info for a specific pet\",\n",
    "        \"operationId\": \"showPetById\",\n",
    "        \"tags\": [\n",
    "          \"pets\"\n",
    "        ],\n",
    "        \"parameters\": [\n",
    "          {\n",
    "            \"name\": \"petId\",\n",
    "            \"in\": \"path\",\n",
    "            \"required\": true,\n",
    "            \"description\": \"The id of the pet to retrieve\",\n",
    "            \"schema\": {\n",
    "              \"type\": \"string\"\n",
    "            }\n",
    "          }\n",
    "        ],\n",
    "        \"responses\": {\n",
    "          \"200\": {\n",
    "            \"description\": \"Expected response to a valid request\",\n",
    "            \"content\": {\n",
    "              \"application/json\": {\n",
    "                \"schema\": {\n",
    "                  \"$ref\": \"#/components/schemas/Pet\"\n",
    "                }\n",
    "              }\n",
    "            }\n",
    "          },\n",
    "          \"default\": {\n",
    "            \"description\": \"unexpected error\",\n",
    "            \"content\": {\n",
    "              \"application/json\": {\n",
    "                \"schema\": {\n",
    "                  \"$ref\": \"#/components/schemas/Error\"\n",
    "                }\n",
    "              }\n",
    "            }\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  },\n",
    "  \"components\": {\n",
    "    \"schemas\": {\n",
    "      \"Pet\": {\n",
    "        \"type\": \"object\",\n",
    "        \"required\": [\n",
    "          \"id\",\n",
    "          \"name\"\n",
    "        ],\n",
    "        \"properties\": {\n",
    "          \"id\": {\n",
    "            \"type\": \"integer\",\n",
    "            \"format\": \"int64\"\n",
    "          },\n",
    "          \"name\": {\n",
    "            \"type\": \"string\"\n",
    "          },\n",
    "          \"tag\": {\n",
    "            \"type\": \"string\"\n",
    "          }\n",
    "        }\n",
    "      },\n",
    "      \"Pets\": {\n",
    "        \"type\": \"array\",\n",
    "        \"maxItems\": 100,\n",
    "        \"items\": {\n",
    "          \"$ref\": \"#/components/schemas/Pet\"\n",
    "        }\n",
    "      },\n",
    "      \"Error\": {\n",
    "        \"type\": \"object\",\n",
    "        \"required\": [\n",
    "          \"code\",\n",
    "          \"message\"\n",
    "        ],\n",
    "        \"properties\": {\n",
    "          \"code\": {\n",
    "            \"type\": \"integer\",\n",
    "            \"format\": \"int32\"\n",
    "          },\n",
    "          \"message\": {\n",
    "            \"type\": \"string\"\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  }\n",
    "}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "653156d4-d6a4-499b-aab3-cf4f7b1f0c47",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Attempting to load an OpenAPI 3.0.0 spec.  This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n"
     ]
    }
   ],
   "source": [
    "# 从text中导入API的详细定义\n",
    "spec = OpenAPISpec.from_text(text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "d9ba6100-3c88-4b05-af93-784b57bf7424",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 转换成openai的fuction call格式\n",
    "pet_openai_functions, pet_callables = openapi_spec_to_openai_fn(spec)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "91199697-c662-438f-8c68-e6daa8aad07d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'name': 'listPets',\n",
       "  'description': 'List all pets',\n",
       "  'parameters': {'type': 'object',\n",
       "   'properties': {'params': {'type': 'object',\n",
       "     'properties': {'limit': {'type': 'integer',\n",
       "       'maximum': 100.0,\n",
       "       'schema_format': 'int32',\n",
       "       'description': 'How many items to return at one time (max 100)'}},\n",
       "     'required': []}}}},\n",
       " {'name': 'createPets',\n",
       "  'description': 'Create a pet',\n",
       "  'parameters': {'type': 'object', 'properties': {}}},\n",
       " {'name': 'showPetById',\n",
       "  'description': 'Info for a specific pet',\n",
       "  'parameters': {'type': 'object',\n",
       "   'properties': {'path_params': {'type': 'object',\n",
       "     'properties': {'petId': {'type': 'string',\n",
       "       'description': 'The id of the pet to retrieve'}},\n",
       "     'required': ['petId']}}}}]"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看fuction的定义\n",
    "pet_openai_functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "ae8b3c1b-7a30-4b1b-abfd-e56f90b1d166",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入模型\n",
    "from langchain.chat_models import ChatOpenAI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "ed100888-f3d4-4739-bba5-f839033850a3",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\14549\\anaconda3\\envs\\basic\\lib\\site-packages\\langchain_core\\_api\\deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.chat_models.openai.ChatOpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import ChatOpenAI`.\n",
      "  warn_deprecated(\n"
     ]
    }
   ],
   "source": [
    "# 设置模型温度系数并传入function\n",
    "model = ChatOpenAI(temperature=0).bind(functions=pet_openai_functions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "27d7817c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"params\":{\"limit\":3}}', 'name': 'listPets'}})"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 输入query，查看模型调用的function以及返回信息\n",
    "model.invoke(\"这三只宠物的名字叫什么？\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "6a373750",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"path_params\":{\"petId\":\"42\"}}', 'name': 'showPetById'}})"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.invoke(\"告诉我id为42的宠物的消息\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "198b21dd-c9de-491d-9a0c-71ae56727689",
   "metadata": {},
   "source": [
    "# 3、 路由\n",
    "\n",
    "展示一个函数调用的例子，用于在两个候选函数之间做出决策。\n",
    "\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "99c30e7d",
   "metadata": {},
   "source": [
    "## 3.1-Tool转换为Function\n",
    "\n",
    "鉴于我们上面提到的工具，让我们将它们格式化为 OpenAI 函数，并展示相同的行为。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "8137758c-c5d3-47df-9062-5e31d43657e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将工具格式化为 OpenAI 函数\n",
    "functions = [\n",
    "    format_tool_to_openai_function(f) for f in [\n",
    "        search_wikipedia, get_current_temperature\n",
    "    ]\n",
    "]\n",
    "model = ChatOpenAI(temperature=0).bind(functions=functions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "99120542-36bc-4ed1-aa9e-e2294e282c81",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"latitude\":37.7749,\"longitude\":-122.4194}', 'name': 'get_current_temperature'}})"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 模型调用\n",
    "model.invoke(\"圣佛朗西斯科现在的温度是多少？\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "a20047d9-4b34-4e6c-9407-570af1559bc8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"query\":\"Langchain\"}', 'name': 'search_wikipedia'}})"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 模型调用\n",
    "model.invoke(\"什么是langchain？\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "831a0506-1d5c-4bc3-b67f-24e201a4fa6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用template构造prompt\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是个乐于助人的助理\"),\n",
    "    (\"user\", \"{input}\"),\n",
    "])\n",
    "\n",
    "# 创建处理链，将 prompt和model连接起来\n",
    "chain = prompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "d9c32ccc-6691-4dd5-98dc-aabf02563ad5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"latitude\":37.7749,\"longitude\":-122.4194}', 'name': 'get_current_temperature'}})"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 输入query进行调用\n",
    "chain.invoke({\"input\": \"圣佛朗西斯科现在的温度是多少？\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "921e808c-6618-4d2f-85df-1f3089497724",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入输出解析的包\n",
    "from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "094baef1-4140-41f4-9111-ff9710826e6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建处理链，将 prompt、model 和 OpenAIFunctionsAgentOutputParser 连接起来\n",
    "chain = prompt | model | OpenAIFunctionsAgentOutputParser()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "f105e8e8-d418-4d4a-95eb-52636d4e890f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用\n",
    "result = chain.invoke({\"input\": \"圣佛朗西斯科现在的温度是多少？\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "409b26c0-a1e0-4225-ae2e-396c1f76bf0a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain_core.agents.AgentActionMessageLog"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 打印返回的类型，可以判断是否产生function的调用\n",
    "type(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "9cdde008-4b11-4ab2-a01c-6ae394a7dccf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'get_current_temperature'"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看调用的tool\n",
    "result.tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "66ffb308-3d77-4acb-b4b5-e2b0d38f3860",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'latitude': 37.7749, 'longitude': -122.4194}"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看tool的输入，result.message_log可以查看调用结果\n",
    "result.tool_input"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "07e661b1-6d0d-43b9-9de4-19c6cceff291",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'The current temperature is 9.4°C'"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用的获取温度的工具\n",
    "get_current_temperature(result.tool_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "057e3072-a203-4a5e-b1f6-b250cd7bd33b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 继续调用\n",
    "result = chain.invoke({\"input\": \"你好!\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "5be6bc0f-5b81-49fa-ac16-b8896847d87a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain_core.agents.AgentFinish"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 打印返回的类型，可以判断是否产生function的调用\n",
    "type(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "dd128d20-f552-4cc5-a45e-f47b58c9982b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'output': 'Well, hello there! How can I assist you today?'}"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 查看返回值\n",
    "result.return_values"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "f75da727",
   "metadata": {},
   "source": [
    "## 3.2-通过route进行tools的选择"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "134f422a-b72b-40df-9552-cf9f9fc2d780",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "route会根据result进行tools的选择：\n",
    "AgentFinish：表示已经完成，可以输出\n",
    "AgentActionMessageLog：表示未完成，需要继续进行route调用tools\n",
    "\"\"\"\n",
    "from langchain.schema.agent import AgentFinish\n",
    "def route(result):\n",
    "    if isinstance(result, AgentFinish):\n",
    "        return result.return_values['output']\n",
    "    else:\n",
    "        tools = {\n",
    "            \"search_wikipedia\": search_wikipedia, \n",
    "            \"get_current_temperature\": get_current_temperature,\n",
    "        }\n",
    "        return tools[result.tool].run(result.tool_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "11b727ca-f571-4410-8087-95c73a37c620",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = prompt | model | OpenAIFunctionsAgentOutputParser() | route"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "da06e7e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'你好！有什么可以帮助你的吗？'"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"input\": \"你好！\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "e6385c8e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'The current temperature is 9.4°C'"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = chain.invoke({\"input\": \"圣弗朗西斯科的天气现在怎么样？\"})\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "5c0de3e7-2367-4cef-b3dd-efa69b701061",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Page: LangChain\\nSummary: LangChain is a framework designed to simplify the creation of applications using large language models (LLMs). As a language model integration framework, LangChain\\'s use-cases largely overlap with those of language models in general, including document analysis and summarization, chatbots, and code analysis.\\n\\nPage: OpenAI\\nSummary: OpenAI is a U.S. based artificial intelligence (AI) research organization founded in December 2015, researching artificial intelligence with the goal of developing \"safe and beneficial\" artificial general intelligence, which it defines as \"highly autonomous systems that outperform humans at most economically valuable work\".\\nAs one of the leading organizations of the AI spring, it has developed several large language models, advanced image generation models, and previously, released open-source models. Its release of ChatGPT has been credited with starting the AI spring.The organization consists of the non-profit OpenAI, Inc. registered in Delaware and its for-profit subsidiary OpenAI Global, LLC. It was founded by Ilya Sutskever, Greg Brockman, Trevor Blackwell, Vicki Cheung, Andrej Karpathy, Durk Kingma, Jessica Livingston, John Schulman, Pamela Vagata, and Wojciech Zaremba, with Sam Altman and Elon Musk serving as the initial board members. Microsoft provided OpenAI Global LLC with a $1 billion investment in 2019 and a $10 billion investment in 2023, with a significant portion of the investment in the form of computational resources on Microsoft\\'s Azure cloud service.On November 17, 2023, the board removed Altman as CEO, while Brockman was removed as chairman and then resigned as president. Four days later, both returned after negotiations with the board, and most of the board members resigned. The new initial board included former Salesforce co-CEO Bret Taylor as chairman. It was also announced that Microsoft will have a non-voting board seat.\\n\\nPage: DataStax\\nSummary: DataStax, Inc. is a real-time data for AI company based in Santa Clara, California. Its product Astra DB is a cloud database-as-a-service based on Apache Cassandra. DataStax also offers DataStax Enterprise (DSE), an on-premises database built on Apache Cassandra, and Astra Streaming, a messaging and event streaming cloud service based on Apache Pulsar. As of June 2022, the company has roughly 800 customers distributed in over 50 countries.'"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result = chain.invoke({\"input\": \"什么是langchain?\"})\n",
    "result"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "c1d0ffc6",
   "metadata": {},
   "source": [
    "# 四、英文版提示\n",
    "\n",
    "我们总结一下完整的调用流程：\n",
    "\n",
    "构造Prompt --> 调用模型 --> 解析模型返回的结果 --> 进行路由选择对应的tool\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "b82fa885",
   "metadata": {},
   "outputs": [],
   "source": [
    "@tool\n",
    "def search(query: str) -> str:\n",
    "    \"\"\"\"Search for weather online\"\"\"\n",
    "    return \"42f\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "74ae5b0c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "search\n",
      "search(query: str) -> str - \"Search for weather online\n",
      "{'query': {'title': 'Query', 'type': 'string'}}\n"
     ]
    }
   ],
   "source": [
    "# 搜索工具的函数名\n",
    "print(search.name)\n",
    "#搜索工具的功能描述（即函数注释）\n",
    "print(search.description)\n",
    "# 搜索工具需要传递的参数\n",
    "print(search.args)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "c1ceebc1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pydantic import BaseModel, Field\n",
    "\n",
    "class SearchInput(BaseModel):\n",
    "    query: str = Field(description=\"Thing to search for\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "1a00da2e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'query': {'title': 'Query', 'type': 'string'}}"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "search.args"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "b0afe28d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'42f'"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "search.run(\"sf\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "c2bf3d23",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入所需的库\n",
    "import requests\n",
    "from pydantic import BaseModel, Field\n",
    "import datetime\n",
    "\n",
    "# 定义输入类（input schema）\n",
    "class OpenMeteoInput(BaseModel):\n",
    "    latitude: float = Field(..., description=\"Latitude of the location to fetch weather data for\") #要获取天气数据的位置的纬度\n",
    "    longitude: float = Field(..., description=\"Longitude of the location to fetch weather data for\") #要获取天气数据的位置的经度\n",
    "\n",
    "# 使用 @tool 装饰器并指定输入模型\n",
    "@tool(args_schema=OpenMeteoInput)\n",
    "def get_current_temperature(latitude: float, longitude: float) -> dict:\n",
    "    \"\"\"\"Fetch current temperature for given coordinates.\"\"\"\n",
    "    \n",
    "    # Open Meteo API 的URL\n",
    "    BASE_URL = \"https://api.open-meteo.com/v1/forecast\"\n",
    "    \n",
    "    # 请求参数\n",
    "    params = {\n",
    "        'latitude': latitude,\n",
    "        'longitude': longitude,\n",
    "        'hourly': 'temperature_2m',\n",
    "        'forecast_days': 1,\n",
    "    }\n",
    "\n",
    "    # 发送 API 请求\n",
    "    response = requests.get(BASE_URL, params=params)\n",
    "    \n",
    "    # 检查响应状态码\n",
    "    if response.status_code == 200:\n",
    "        # 解析 JSON 响应\n",
    "        results = response.json()\n",
    "    else:\n",
    "        # 处理请求失败的情况\n",
    "        raise Exception(f\"API Request failed with status code: {response.status_code}\")\n",
    "\n",
    "    # 获取当前 UTC 时间\n",
    "    current_utc_time = datetime.datetime.utcnow()\n",
    "    \n",
    "    # 将时间字符串转换为 datetime 对象\n",
    "    time_list = [datetime.datetime.fromisoformat(time_str.replace('Z', '+00:00')) for time_str in results['hourly']['time']]\n",
    "    \n",
    "    # 获取温度列表\n",
    "    temperature_list = results['hourly']['temperature_2m']\n",
    "    \n",
    "    # 找到最接近当前时间的索引\n",
    "    closest_time_index = min(range(len(time_list)), key=lambda i: abs(time_list[i] - current_utc_time))\n",
    "    \n",
    "    # 获取当前温度\n",
    "    current_temperature = temperature_list[closest_time_index]\n",
    "    \n",
    "    # 返回当前温度的字符串形式\n",
    "    return f'The current temperature is {current_temperature}°C'\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "130925b6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "get_current_temperature\n",
      "get_current_temperature(latitude: float, longitude: float) -> dict - \"Fetch current temperature for given coordinates.\n",
      "{'latitude': {'title': 'Latitude', 'description': 'Latitude of the location to fetch weather data for', 'type': 'number'}, 'longitude': {'title': 'Longitude', 'description': 'Longitude of the location to fetch weather data for', 'type': 'number'}}\n"
     ]
    }
   ],
   "source": [
    "# 工具的名字\n",
    "print(get_current_temperature.name)\n",
    "# 工具的功能描述\n",
    "print(get_current_temperature.description)\n",
    "# 工具的输入参数\n",
    "print(get_current_temperature.args)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "122e1ef7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': 'get_current_temperature',\n",
       " 'description': 'get_current_temperature(latitude: float, longitude: float) -> dict - \"Fetch current temperature for given coordinates.',\n",
       " 'parameters': {'type': 'object',\n",
       "  'properties': {'latitude': {'description': 'Latitude of the location to fetch weather data for',\n",
       "    'type': 'number'},\n",
       "   'longitude': {'description': 'Longitude of the location to fetch weather data for',\n",
       "    'type': 'number'}},\n",
       "  'required': ['latitude', 'longitude']}}"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 导入openai的模板\n",
    "from langchain.tools.render import format_tool_to_openai_function\n",
    "\n",
    "format_tool_to_openai_function(get_current_temperature)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "d4e68f7e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'The current temperature is 41.4°C'"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "get_current_temperature({\"latitude\": 13, \"longitude\": 14})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "d7477093",
   "metadata": {},
   "outputs": [],
   "source": [
    "import wikipedia\n",
    "\n",
    "# 定义维基百科搜索的tool\n",
    "@tool\n",
    "def search_wikipedia(query: str) -> str:\n",
    "    \"\"\"Run Wikipedia search and get page summaries.\"\"\"\n",
    "    page_titles = wikipedia.search(query)\n",
    "    summaries = []\n",
    "    for page_title in page_titles[: 3]: #取前三个页面标题\n",
    "        try:\n",
    "            #使用 wikipedia 模块的 page 函数，获取指定标题的维基百科页面对象。\n",
    "            wiki_page =  wikipedia.page(title=page_title, auto_suggest=False) \n",
    "            # 获取页面摘要\n",
    "            summaries.append(f\"Page: {page_title}\\nSummary: {wiki_page.summary}\")\n",
    "        except (\n",
    "            self.wiki_client.exceptions.PageError,\n",
    "            self.wiki_client.exceptions.DisambiguationError,\n",
    "        ):\n",
    "            pass\n",
    "    if not summaries:\n",
    "        return \"No good Wikipedia Search Result was found\"\n",
    "    return \"\\n\\n\".join(summaries)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "2dd47d14",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': 'search_wikipedia',\n",
       " 'description': 'search_wikipedia(query: str) -> str - Run Wikipedia search and get page summaries.',\n",
       " 'parameters': {'type': 'object',\n",
       "  'properties': {'query': {'type': 'string'}},\n",
       "  'required': ['query']}}"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 将工具格式化为 OpenAI 函数\n",
    "format_tool_to_openai_function(search_wikipedia)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "5edf4ce8",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\anaconda\\Lib\\site-packages\\langchain_core\\_api\\deprecation.py:117: LangChainDeprecationWarning: The class `langchain_community.chat_models.openai.ChatOpenAI` was deprecated in langchain-community 0.0.10 and will be removed in 0.2.0. An updated version of the class exists in the langchain-openai package and should be used instead. To use it run `pip install -U langchain-openai` and import as `from langchain_openai import ChatOpenAI`.\n",
      "  warn_deprecated(\n"
     ]
    }
   ],
   "source": [
    "from langchain.chat_models import ChatOpenAI\n",
    "# 将工具格式化为 OpenAI 函数\n",
    "functions = [\n",
    "    format_tool_to_openai_function(f) for f in [\n",
    "        search_wikipedia, get_current_temperature\n",
    "    ]\n",
    "]\n",
    "model = ChatOpenAI(temperature=0).bind(functions=functions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "bf91812d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用template构造prompt\n",
    "from langchain.prompts import ChatPromptTemplate\n",
    "prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"You are helpful but sassy assistant\"),\n",
    "    (\"user\", \"{input}\"),\n",
    "])\n",
    "\n",
    "# 创建处理链，将 prompt和model连接起来\n",
    "chain = prompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "8fefc329-b270-4ebb-b884-430e4e541e7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入输出解析的包\n",
    "from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser\n",
    "\n",
    "chain = prompt | model | OpenAIFunctionsAgentOutputParser() | route"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "f8ea3f45",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用\n",
    "result = chain.invoke({\"input\": \"what is the weather in sf right now\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "1d772b84",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'The current temperature is 8.8°C'"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "a353363e-27b8-440f-a8b7-31bc2e861a42",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Well, hello there! How can I assist you today?'"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用并查看结果\n",
    "chain.invoke({\"input\": \"hi!\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "6c93eb1b-044f-4c52-bcd4-dcd0e01f42e8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Page: LangChain\\nSummary: LangChain is a framework designed to simplify the creation of applications using large language models (LLMs). As a language model integration framework, LangChain\\'s use-cases largely overlap with those of language models in general, including document analysis and summarization, chatbots, and code analysis.\\n\\nPage: OpenAI\\nSummary: OpenAI is a U.S. based artificial intelligence (AI) research organization founded in December 2015, researching artificial intelligence with the goal of developing \"safe and beneficial\" artificial general intelligence, which it defines as \"highly autonomous systems that outperform humans at most economically valuable work\".\\nAs one of the leading organizations of the AI spring, it has developed several large language models, advanced image generation models, and previously, released open-source models. Its release of ChatGPT has been credited with starting the AI spring.The organization consists of the non-profit OpenAI, Inc. registered in Delaware and its for-profit subsidiary OpenAI Global, LLC. It was founded by Ilya Sutskever, Greg Brockman, Trevor Blackwell, Vicki Cheung, Andrej Karpathy, Durk Kingma, Jessica Livingston, John Schulman, Pamela Vagata, and Wojciech Zaremba, with Sam Altman and Elon Musk serving as the initial board members. Microsoft provided OpenAI Global LLC with a $1 billion investment in 2019 and a $10 billion investment in 2023, with a significant portion of the investment in the form of computational resources on Microsoft\\'s Azure cloud service.On November 17, 2023, the board removed Altman as CEO, while Brockman was removed as chairman and then resigned as president. Four days later, both returned after negotiations with the board, and most of the board members resigned. The new initial board included former Salesforce co-CEO Bret Taylor as chairman. It was also announced that Microsoft will have a non-voting board seat.\\n\\nPage: DataStax\\nSummary: DataStax, Inc. is a real-time data for AI company based in Santa Clara, California. Its product Astra DB is a cloud database-as-a-service based on Apache Cassandra. DataStax also offers DataStax Enterprise (DSE), an on-premises database built on Apache Cassandra, and Astra Streaming, a messaging and event streaming cloud service based on Apache Pulsar. As of June 2022, the company has roughly 800 customers distributed in over 50 countries.'"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用\n",
    "result = chain.invoke({\"input\": \"langchain?\"})\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "b2dcb4b2-0e0d-425b-bff8-db7cd33afa16",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"latitude\":37.7749,\"longitude\":-122.4194}', 'name': 'get_current_temperature'}})"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用\n",
    "result = chain.invoke({\"input\": \"What is the weather in san francisco right now?\"})\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "139c45e6-65a4-4fcb-a109-e5ba41a80835",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入tool包\n",
    "from langchain.agents import tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "7760cfaa",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.schema.agent import AgentFinish\n",
    "def route(result):\n",
    "    if isinstance(result, AgentFinish):\n",
    "        return result.return_values['output']\n",
    "    else:\n",
    "        tools = {\n",
    "            \"search_wikipedia\": search_wikipedia, \n",
    "            \"get_current_temperature\": get_current_temperature,\n",
    "        }\n",
    "        return tools[result.tool].run(result.tool_input)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "89d8b769",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = prompt | model | OpenAIFunctionsAgentOutputParser() | route"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "67be1657",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Well, hello there! How can I assist you today?'"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用并查看结果\n",
    "chain.invoke({\"input\": \"hi!\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8bcd38a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用\n",
    "result = chain.invoke({\"input\": \"What is langchain?\"})\n",
    "result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "bcabd4a8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'The current temperature is 8.8°C'"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 调用\n",
    "result = chain.invoke({\"input\": \"What is the weather in san francisco right now?\"})\n",
    "result"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
